﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Chapter17
{
    using System.Windows.Forms;
    using System.IO;
    using System.Threading;
    using System.Reflection;

    partial class Program
    {
        private delegate void Feedback(int value);

        internal static void Begin(Action action, bool active = false)
        {
            if (active)
            {
                Console.WriteLine("开始演示：");
                action();
                Console.WriteLine("演示完毕：");
                Console.ReadLine();
            }
        }

        private static void StaticDelegateDemo()
        {
            Console.WriteLine("------ Static Delegate Demo ------");
            Counter(1, 3, null);
            Counter(1, 3, new Feedback(FeedbackToConsole));
            Counter(1, 3, new Feedback(FeedbackToMsgBox));
            Console.WriteLine();
        }

        private static void InstatanceDelegateDemo()
        {
            Console.WriteLine("------ Instance Delegate Demo ------");
            var p = new Program();
            Counter(1, 3, new Feedback(p.FeedbackToFile));
            Console.WriteLine();
        }

        private static void ChainDelegateDemo1(Program p)
        {
            Console.WriteLine("------ Chain Delegate Demo 1 ------");
            Feedback fb1 = new Feedback(FeedbackToConsole);
            Feedback fb2 = new Feedback(FeedbackToMsgBox);
            Feedback fb3 = new Feedback(p.FeedbackToFile);
            Feedback fbChain = null;
            fbChain = (Feedback)Delegate.Combine(fbChain, fb1);
            fbChain = (Feedback)Delegate.Combine(fbChain, fb2);
            fbChain = (Feedback)Delegate.Combine(fbChain, fb3);
            Counter(1, 2, fbChain);
            Console.WriteLine();
            fbChain = (Feedback)Delegate.Remove(fbChain, new Feedback(FeedbackToMsgBox));
            Counter(1, 2, fbChain);
        }

        private static void ChainDelegateDemo2(Program p)
        {
            Console.WriteLine("------ Chain Delegate Demo 2 ------");
            Feedback fb1 = new Feedback(FeedbackToConsole);
            Feedback fb2 = new Feedback(FeedbackToMsgBox);
            Feedback fb3 = new Feedback(p.FeedbackToFile);
            Feedback fbChain = null;
            fbChain += fb1;
            fbChain += fb2;
            fbChain += fb3;
            Counter(1, 2, fbChain);
            Console.WriteLine();
            fbChain -= new Feedback(FeedbackToMsgBox);
            Counter(1, 2, fbChain);
        }

        private static void Counter(int from, int to, Feedback fb)
        {
            for (int i = from; i < to; i++)
            {
                if (fb != null)
                    fb(i);
            }
        }

        private static void FeedbackToConsole(int value)
        {
            Console.WriteLine("Item=" + value);
        }

        private static void FeedbackToMsgBox(int value)
        {
            MessageBox.Show("Item=" + value);
        }

        private void FeedbackToFile(int value)
        {
            StreamWriter sw = new StreamWriter("Status.txt", true);
            sw.WriteLine("Item=" + value);
            sw.Close();
        }

        internal static void C01()
        {
            StaticDelegateDemo();
            InstatanceDelegateDemo();
            ChainDelegateDemo1(new Program());
            ChainDelegateDemo2(new Program());
        }

        private sealed class Light
        {
            internal string SwitchPosition()
            {
                return "The light is off";
            }
        }

        private sealed class Fan
        {
            internal string Speed()
            {
                throw new InvalidOperationException("The fan broke due to overheating");
            }
        }

        private sealed class Speaker
        {
            internal string Volume()
            {
                return "The volume is loud";
            }
        }

        private delegate string GetStatus();

        internal static void C05()
        {
            GetStatus getStatus = null;
            getStatus += new GetStatus(new Light().SwitchPosition);
            getStatus += new GetStatus(new Fan().Speed);
            getStatus += new GetStatus(new Speaker().Volume);
            Console.WriteLine(GetComponentStatusReport(getStatus));
        }

        private static string GetComponentStatusReport(GetStatus status)
        {
            if (status == null) return null;
            StringBuilder report = new StringBuilder();
            Delegate[] arrayOfDelegate = status.GetInvocationList();
            foreach (GetStatus getStatus in arrayOfDelegate)
            {
                try
                {
                    report.AppendFormat("{0}{1}{1}", getStatus(), Environment.NewLine);
                }
                catch (InvalidOperationException e)
                {
                    object component = getStatus.Target;
                    report.AppendFormat(
                        "Failed to get status from {1}{2}{0}  Error: {3}{0}{0}",
                        Environment.NewLine,
                        (component == null) ? "" : component.GetType() + ".",
                        getStatus.Method.Name,
                        e.Message);
                }
            }
            return report.ToString();
        }

        internal static void C07()
        {
            {
                AClass.CallbackWithoutNewingADelegateObject();
                AClass.UsingLocalVariablesInTheCallbackCode(10);
            }
            {
                string[] names = { "Jeff", "Kristin", "Aidan", "Grant" };
                char charToFind = 'a';
                names = Array.FindAll(names, name => name.IndexOf(charToFind) >= 0);
                names = Array.ConvertAll(names, name => name.ToUpper());
                Array.ForEach(names, Console.WriteLine);
            }
        }

        private sealed class AClass
        {
            internal static void CallbackWithoutNewingADelegateObject()
            {
                ThreadPool.QueueUserWorkItem(SomeAsyncTask, 5);
            }

            private static void SomeAsyncTask(object obj)
            {
                Console.WriteLine(obj);
            }

            internal static void CallbackWithoutNewingADelegateObject2()
            {
                ThreadPool.QueueUserWorkItem(obj => Console.WriteLine(obj), 5);
            }

            internal static void UsingLocalVariablesInTheCallbackCode(int numToDo)
            {
                int[] squares = new int[numToDo];
                AutoResetEvent done = new AutoResetEvent(false);
                for (int i = 0; i < squares.Length; i++)
                {
                    ThreadPool.QueueUserWorkItem(obj =>
                    {
                        int num = (int)obj;
                        squares[num] = num * num;
                        if (Interlocked.Decrement(ref numToDo) == 0)
                            done.Set();
                    }, i);
                }
                done.WaitOne();
                for (int i = 0; i < squares.Length; i++)
                    Console.WriteLine("Index {0}, Square={1}", i, squares[i]);
            }
        }

        internal static void C08()
        {
            string[] args = Environment.GetCommandLineArgs();
            if (args.Length < 2)
            {
                string fileName = Path.GetFileNameWithoutExtension(Assembly.GetEntryAssembly().Location);
                string usage =
                    @"Usage:" +
                    "{0}{1} delType methodName [Arg1] [Arg2]" +
                    "{0} where delType must be TwoInt32s or OneString" +
                    "{0} if delType is TwoInt32s, methodName must be Add or Subtract" +
                    "{0} if delType is OneString, methodName must be NumChars or Reverse" +
                    "{0}" +
                    "{0}Examples:" +
                    "{0}    {1} TwoInt32s Add 123 321" +
                    "{0}    {1} TwoInt32s Subtract 123 321" +
                    "{0}    {1} OneString NumChars \"Hello there\"" +
                    "{0}    {1} OntString Reverse \"Hello there\"";
                Console.WriteLine(usage, Environment.NewLine, fileName);
                return;
            }
            Array.Copy(args, 1, args, 0, args.Length - 1);
            Array.Clear(args, args.Length - 1, 1);
            Type delType = Type.GetType(args[0]);
            if (delType == null)
            {
                Console.WriteLine("Invalid delType argument: " + args[0]);
                return;
            }
            Delegate d;
            try
            {
                MethodInfo mi = typeof(Program).GetMethod(args[1], BindingFlags.NonPublic | BindingFlags.Static);
                d = Delegate.CreateDelegate(delType, mi);
            }
            catch (ArgumentException)
            {
                Console.WriteLine("Invalid methodName argument: " + args[1]);
                return;
            }
            object[] callbackArgs = new object[args.Length - 2];
            if (d.GetType() == typeof(TwoInt32s))
            {
                try
                {
                    for (int i = 0; i < args.Length; i++)
                    {
                        callbackArgs[i - 2] = int.Parse(args[i]);
                    }
                }
                catch (FormatException)
                {
                    Console.WriteLine("Parameters must be integer.");
                    return;
                }
            }
            if (d.GetType() == typeof(OneString))
            {
                Array.Copy(args, 2, callbackArgs, 0, callbackArgs.Length);
            }
            try
            {
                object result = d.DynamicInvoke(callbackArgs);
                Console.WriteLine("Result = ", result);
            }
            catch (TargetParameterCountException)
            {
                Console.WriteLine("Incorrect number of parameters specified.");
            }
        }

        private delegate object TwoInt32s(int n1, int n2);
        private delegate object OneString(string sl);

        private static object Add(int n1, int n2)
        {
            return n1 + n2;
        }

        private static object Subtract(int n1, int n2)
        {
            return n1 - n2;
        }

        private static object NumChars(string s1)
        {
            return s1.Length;
        }

        private static object Reverse(string s1)
        {
            char[] chars = s1.ToCharArray();
            Array.Reverse(chars);
            return new string(chars);
        }
    }
}
